package renderer;

import org.joml.Matrix4f;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import static org.lwjgl.opengl.GL11.GL_FALSE;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;

public class Shader {

    private int shaderProgramId;

    private String vertexSource;

    private String fragmentSource;

    private String filePath;

    public Shader(String filepath) {
        this.filePath = filepath;
        try {
            String source = new String(Files.readAllBytes(Paths.get(filepath)));
            String[] split = source.split("(#type)( )+([a-zA-Z]+)");

            int index = source.indexOf("#type") + 6;
            int eol = source.indexOf("\r\n", index);
            String firstPattern = source.substring(index, eol).trim();

            index = source.indexOf("#type", eol) + 6;
            eol = source.indexOf("\r\n", index);
            String secondPattern = source.substring(index, eol).trim();

            if (firstPattern.equals("vertex")) {
                vertexSource = split[1];
            } else if (firstPattern.equals("fragment")) {
                fragmentSource = split[1];
            } else {
                throw new RuntimeException("Error: Invalid shader type specified");
            }

            if (secondPattern.equals("vertex")) {
                vertexSource = split[2];
            } else if (secondPattern.equals("fragment")) {
                fragmentSource = split[2];
            } else {
                throw new RuntimeException("Error: Invalid shader type specified");
            }
        } catch (IOException e) {
            e.printStackTrace();
            assert false : "Error: Could not open file for shader";
        }

    }

    public void use() {
        glUseProgram(shaderProgramId);
    }

    /**
     * 编译链接着色器
     */
    public void compile() {
        int vertexID, fragmentID;
        //加载并且编译顶点着色器
        vertexID = glCreateShader(GL_VERTEX_SHADER);
        //传输着色器源码
        glShaderSource(vertexID, vertexSource);
        glCompileShader(vertexID);

        //检查编译是否成功
        int success = glGetShaderi(vertexID, GL_COMPILE_STATUS);
        if (success == GL_FALSE) {
            int len = glGetShaderi(vertexID, GL_INFO_LOG_LENGTH);
            System.out.println("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" + filePath + glGetShaderInfoLog(vertexID, len));
            assert false : "ERROR::SHADER::VERTEX::COMPILATION_FAILED";
        }
        //加载并且编译片段着色器
        fragmentID = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentID, fragmentSource);
        glCompileShader(fragmentID);
        success = glGetShaderi(fragmentID, GL_COMPILE_STATUS);
        if (success == GL_FALSE) {
            int len = glGetShaderi(fragmentID, GL_INFO_LOG_LENGTH);
            System.out.println("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" + filePath + glGetShaderInfoLog(fragmentID, len));
            assert false : "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED";
        }

        //创建着色器程序
        shaderProgramId = glCreateProgram();
        glAttachShader(shaderProgramId, vertexID);
        glAttachShader(shaderProgramId, fragmentID);
        glLinkProgram(shaderProgramId);

        //检查链接是否成功
        success = glGetProgrami(shaderProgramId, GL_LINK_STATUS);
        if (success == GL_FALSE) {
            int len = glGetProgrami(shaderProgramId, GL_INFO_LOG_LENGTH);
            System.out.println("ERROR::SHADER::PROGRAM::LINKING_FAILED\n" + glGetProgramInfoLog(shaderProgramId, len));
            assert false : "ERROR::SHADER::PROGRAM::LINKING_FAILED";
        }

    }

    public void detach() {
        glUseProgram(0);
    }

    public void uploadMat4f(String varName, Matrix4f mat4) {
        int varLocation = glGetUniformLocation(shaderProgramId, varName);
        glUniformMatrix4fv(varLocation, false, mat4.get(new float[16]));

    }
}
